package com.sixbro.shiro.jwt.utils;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.bxs.crypto.AesUtils;
import com.bxs.rbac.shiro.jwt.domain.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.UUID;

/**
 * <p>
 *
 * </p>
 *
 * @Author: Mr.Lu
 * @Since: 2020/6/17 16:40
 */
public class JWTUtil {

    private static final Logger _log = LoggerFactory.getLogger(JWTUtil.class);

    // 过期时间5分钟
    private static final long EXPIRE_TIME = 5*60*1000;


    /**
     * 校验token是否正确
     * @param token 密钥
     * @param secret 用户的密码
     * @return 是否正确
     */
    public static boolean verify(String token, String username, String secret) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("username", username)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception exception) {
            return false;
        }
    }

    /**
     * 获得token中的信息无需secret解密也能获得
     * @return token中包含的用户名
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 生成签名,5min后过期
     * @param username 用户名
     * @param secret 用户的密码
     * @return 加密的token
     */
    public static String sign(String username, String secret) {
        Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
        Algorithm algorithm = Algorithm.HMAC256(secret);
        // 附带username信息
        return JWT.create()
                .withClaim("username", username)
                .withExpiresAt(date)
                .sign(algorithm);
    }


    /*******************************************
     ********* io.jsonwebtoken *****************
     *******************************************/


    private static final String SECRET = "oaighnqq;ihasdohqa;";
    private static final Long EXPIRATION = 24*60*60*1000L;

    public static String generate( Object principal ) throws NoSuchPaddingException, BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeyException {
        User user = (User) principal;
        user.setPassword( null );
        String encrypted = AesUtils.encrypt16( JSONObject.toJSONString( user ) );
        return Jwts.builder( )
                .claim("id", UUID.randomUUID().toString() )
                .setSubject( encrypted )
                .claim("cre", new Date( ) )
                .setExpiration( getExpirationDate( ) )
                .signWith( SignatureAlgorithm.HS512, SECRET )
                .compact( );
    }

    public static User getUserDetail(String token ) throws IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException {
        final Claims claims = getClaims( token );
        String subject = claims.getSubject( );
        String aesDecrypt16 = AesUtils.decrypt16( subject );
        JSONObject jsonObject = JSONObject.parseObject( aesDecrypt16 );
        return JSONObject.toJavaObject( jsonObject, User.class );
    }

    private static Date getExpirationDate( ) {
        return new Date( System.currentTimeMillis( ) + EXPIRATION );
    }

    private static Claims getClaims( String token ) {
        return Jwts.parser( )
                .setSigningKey( SECRET )
                .parseClaimsJws( token )
                .getBody( );
    }
}
